AWS LambdaからAmazon Bedrockを呼び出す際、S3に保存したプロンプトファイルを参照させる
はじめに
AWS LambdaからAmazon Bedrockを呼び出す際、事前にS3バケットに保存したプロンプトファイルを参照させる方法をまとめました。
プロンプト部分のみをLambdaのコードから切り離すことで、コードの修正することなく、生成AIへのプロンプトを変更することができます。
利用想定シーンとしては、開発者と運用者が異なる場合に、運用者がプロンプトをチューニングしたいケースです。
以下に、構成の概要を示します。
Bedrock
東京リージョンで、利用したいモデルを有効化します。今回は、Claudeが利用できるよう設定しました。
S3バケット
S3バケットに、prompt.txt
というファイル名でプロンプトを記載しアップロードします。
プロンプト内容は、以下の通りです。
あなたは、レストランの電話担当者です。 お客様からのお問い合わせ内容を元に、ルール内のリストからもっとも適切な種別を選んでください。 存在しない問い合わせ種別に遭遇した場合は、「その他」を回答してください。 <rule> 1. 返信には、「以下のように変換しました」等の文言は含めないでください。 2. お客様のお問い合わせ内容に最も適した種別を次のリストから選択してください: - メニューに関する問い合わせ - 大人数対応に関する問い合わせ - 予約に関する問い合わせ - 営業日や時間に関する問い合わせ 3. 回答は、選択した種別のみを返してしてください。 </rule> お客さんのお問い合わせ内容は次のとおりです。 <question> {input_text} </question>
元ネタは、以下のブログです。Amazon Connectを利用した電話でのお問い合わせをAIチャットボットが対応し、Amazon BedrockのClaudeを用いてお問い合わせの種別判定を行うときに利用したプロンプトです。
プロンプトの内容を変更したい場合は、prompt.txt
ファイルを編集してS3バケットに再アップロードするだけです。
Lambda
Lambdaを以下の設定で作成します。
- ランタイム:Python 3.12
- タイムアウト:20秒
- IAMロールに追加するIAMポリシー
- AmazonBedrockFullAccess
- AmazonS3FullAccess
import json import boto3 s3 = boto3.client('s3') bedrock_runtime = boto3.client('bedrock-runtime') def get_prompt_from_s3(bucket_name, file_key): response = s3.get_object(Bucket=bucket_name, Key=file_key) prompt = response['Body'].read().decode('utf-8') return prompt def lambda_handler(event, context): bucket_name = 'バケット名' file_key = 'prompt.txt' # S3からプロンプトを取得 prompt_body = get_prompt_from_s3(bucket_name, file_key) input_text = event['question'] # S3のプロンプトを組み合わせて最終的なプロンプトを作成 prompt = f"\n\nHuman: {prompt_body}\n<question>\n{input_text}\n</question>\n\nAssistant:" print("Received prompt:" + json.dumps(prompt, ensure_ascii=False)) accept = 'application/json' contentType = 'application/json' modelId = 'anthropic.claude-instant-v1' # modelId = 'anthropic.claude-v2:1' body = json.dumps({ 'prompt': prompt, 'max_tokens_to_sample': 1000, 'temperature': 0, }) response = bedrock_runtime.invoke_model( modelId=modelId, accept=accept, contentType=contentType, body=body ) response_body = json.loads(response.get('body').read()) return { 'statusCode': 200, 'body': response_body.get('completion').strip() }
- 全体の流れとしては、S3バケットのプロンプトファイルのテキストを取得し、そのテキストをプロンプトとしてBedrock Claudeモデルを呼び出し、種別判定を行います。
- 変数名
bucket_name
は、使用するS3バケット名を指定します。 - Claude 2.1ではなく、Claude Instantモデルを利用しています。
- 変数名
input_text
にはユーザーからの問い合わせ内容を挿入します。 - 変数名
prompt
では、固定のフレーズ 「\n\nHuman:」と 「\n\nAssistant:」 を追加し、S3から取得したプロンプトと質問(input_text
)を組み合わせています。
Lambdaを実行
今回の質問は「二十人で予約したいんですが入りますか」とし、Lambdaを実行します。
テストイベントの設定は以下の通りです。
{ "question": "二十人で予約したいんですが入りますか" }
Lambdaを実行すると、結果としてプロンプトは以下のようになります。Lambdaのログからも確認できます。
\n\nHuman:あなたは、レストランの電話担当者です。 お客様からのお問い合わせ内容を元に、ルール内のリストからもっとも適切な種別を選んでください。 存在しない問い合わせ種別に遭遇した場合は、「その他」を回答してください。 <rule> 1. 返信には、「以下のように変換しました」等の文言は含めないでください。 2. お客様のお問い合わせ内容に最も適した種別を次のリストから選択してください: - メニューに関する問い合わせ - 大人数対応に関する問い合わせ - 予約に関する問い合わせ - 営業日や時間に関する問い合わせ 3. 回答は、選択した種別のみを返してしてください。 </rule> お客さんのお問い合わせ内容は次のとおりです。 <question> 二十人で予約したいんですが入りますか </question> \n\nAssistant:
Lambda実行後のレスポンス内容は以下の通りです。
{ "statusCode": 200, "body": "大人数対応に関する問い合わせ" }
お問い合わせ内容(「二十人で予約したいんですが入りますか」)に対して「大人数対応に関する問い合わせ」と種別判定されました。これは正しい判定です。
以上のように、コード修正することなく、プロンプトを容易に変更できます。